#ifndef _INSTRUCTION_H_
#define _INSTRUCTION_H_

#include <iostream>
#include <sstream>
#include <string>
#include <cmath>
#include <cstdlib>

#include "helper.h"

enum opType {NA, REG, MEM, IMM, REGMEM, REGIMM, ALL};

//Condition codes used for forward branches
const unsigned int CONDITION_LENGTH = 7;
const std::string BRANCH_CONDITIONS[CONDITION_LENGTH] = {"AL", "EQ", "NE", "MI", "PL", "VS", "GE"};

class CInstruction
{
public:
	CInstruction(CInstruction *prev);
	virtual ~CInstruction() {};
    	virtual bool isInstruction() = 0;
	virtual std::string getCode() = 0;
	virtual CInstruction* copy() = 0;
	virtual void GetRandomOperands(SGlobalInfo &info) = 0;
    	virtual int getDestReg(int* destReg) = 0;
    	virtual int getSrcReg(int* srcRegs) = 0;

	friend class CISA;
	friend class CProgram;


protected:
	class COperand
	{
	public:
		std::string GetCode();

		void GetRandomOperand();
		void GetRegOperand();
		void GetMemOperand();
		void GetImmOperand();
		unsigned int index;
		opType type;
	};

	CInstruction *prev;
	std::string name;
};

class CInstruction1arg : public CInstruction
{
public:
	CInstruction1arg(CInstruction *prev, opType op1);
	virtual bool isInstruction() {return true;};
	virtual std::string getCode();
	virtual int getDestReg(int* destReg);
    virtual int getSrcReg(int* srcRegs);
	virtual CInstruction* copy();
	virtual void GetRandomOperands(SGlobalInfo &info);
private:
	COperand ops[1];
};

class CInstruction2args : public CInstruction
{
public:
	CInstruction2args(CInstruction *prev, opType op1, opType op2);
	CInstruction2args(CInstruction *prev, unsigned int reg, unsigned int value);

    virtual bool isInstruction() {return true;};
	virtual std::string getCode();
	virtual CInstruction* copy();
	virtual int getDestReg(int* destReg);
    virtual int getSrcReg(int* srcRegs);
	virtual void GetRandomOperands(SGlobalInfo &info);
private:
	COperand ops[2];
};

class CInstruction3args : public CInstruction
{
public:
	CInstruction3args(CInstruction *prev, opType op1, opType op2, opType op3);
	CInstruction3args(CInstruction *prev, unsigned int reg, unsigned int value);

    virtual bool isInstruction() {return true;};
	virtual std::string getCode();
	virtual CInstruction* copy();
	virtual int getDestReg(int* destReg);
    virtual int getSrcReg(int* srcRegs);
	virtual void GetRandomOperands(SGlobalInfo &info);
private:
	COperand ops[3];
};

class CInstructionBranch : public CInstruction
{
public:
	CInstructionBranch(CInstruction *prev);

    virtual bool isInstruction() {return true;};
	virtual std::string getCode();
	virtual CInstruction* copy();
	//Branches will never cause any hazards!
	virtual int getDestReg(int* destReg) {return 0;};
    virtual int getSrcReg(int* srcRegs) {return 0;};
	virtual void GetRandomOperands(SGlobalInfo &info);
private:
	std::string branchName;
};

class CInstructionLabel : public CInstruction
{
public:
	CInstructionLabel(CInstruction *prev, std::string name);

    virtual bool isInstruction() {return false;};
    //these two should never be called.
	virtual int getDestReg(int* destReg) {return 0;};
    virtual int getSrcReg(int* srcRegs) {return 0;};
	virtual std::string getCode();
	virtual CInstruction* copy();
	virtual void GetRandomOperands(SGlobalInfo &info);
private:
	std::string labelName;
};


#endif
